package com.google.appengine.api.datastore;

import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter;

/**
 *
 * @author Secondary User
 */
@Data
public class Key implements Comparable<Key>
{

	@Getter(AccessLevel.NONE)
	private final String namespace;
	private final Key parent;
	private final String kind;
	private final String name;
	private final long id;
	static final long NO_ID = -99227799;
	public final static char SPLIT_CHAR = ':';

	public boolean isComplete()
	{
		if (kind == null || kind.isEmpty())
		{
			return false;
		}
		if (name == null || name.isEmpty())
		{
			return id > 0;
		}
		return true;
	}

	@Override
	public int compareTo(Key o)
	{
		int toReturn = compare(namespace, o.namespace);
		if (toReturn != 0)
		{
			return toReturn;
		}
		toReturn = compare(parent, o.parent);
		if (toReturn != 0)
		{
			return toReturn;
		}
		toReturn = compare(kind, o.kind);
		if (toReturn != 0)
		{
			return toReturn;
		}
		toReturn = compare(name, o.name);
		if (toReturn != 0)
		{
			return toReturn;
		}
		return compare(id, o.id);
	}

	private static int compare(Comparable a, Comparable b)
	{
		if (a == null && b == null)
		{
			return 0;
		}
		if (a == null)
		{
			return -1;
		}
		if (b == null)
		{
			return 1;
		}
		return a.compareTo(b);
	}

	public String toKeyString()
	{
		return toString(this);
	}

	private static String toString(Key key)
	{
		StringBuilder sb = new StringBuilder();
		appendToString(key, sb);
		return sb.toString();
	}

	private static void appendToString(Key key, StringBuilder sb)
	{
		sb.append(key.namespace);
		sb.append(SPLIT_CHAR);
		if (key.parent == null)
		{
			sb.append('K');
		}
		else
		{
			sb.append('P');
			appendToString(key.parent, sb);
		}
		sb.append(SPLIT_CHAR);
		sb.append(key.kind);
		sb.append(SPLIT_CHAR);
		if (key.name == null || key.name.isEmpty())
		{
			if (key.id == NO_ID)
			{
				throw new RuntimeException("Key not complete.");
			}
			sb.append(SPLIT_CHAR);
			sb.append(key.id);
		}
		else
		{
			sb.append(key.name);
		}
	}

	public static Key fromKeyString(String keyString)
	{
		ParseableString ps = new ParseableString(keyString);
		return fromKeyString(ps);
	}

	private static Key fromKeyString(ParseableString ps)
	{
		String namespace = ps.next();
		String kp = ps.next();
		Key parent = null;
		if ("P".equals(kp))
		{
			parent = fromKeyString(ps);
		}
		String kind = ps.next();
		String name = ps.next();
		long id = NO_ID;
		if (name == null || name.isEmpty())
		{
			try
			{
				id = Long.parseLong(ps.next());
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
			return new Key(namespace, parent, kind, null, id);
		}
		else
		{
			return new Key(namespace, parent, kind, name, id);
		}

	}

	private static class ParseableString
	{

		private String str;

		private ParseableString(String str)
		{
			this.str = str;
		}

		private String next()
		{
			System.err.println(str);
			int loc = str.indexOf(SPLIT_CHAR);
			if (loc == -1)
			{
				String toReturn = str;
				str = null;
				return toReturn;
			}
			String toReturn = str.substring(0, loc);
			str = str.substring(loc + 1);
			return toReturn;
		}
	}
	//Pseudocode
//AppId:Namespace:K:Kind
//AppId:Namespace:P:AppId:Namespace:P:AppId:Namespace:K:Kind::id:Kind::id:Kind::id
//
//toString()
//appid
//:
//namespace
//:
//if (parent == null)
//	k
//else
//	p
//	:
//	toString(parent)
//:
//kind
//::id
//:name
//
//fromString()
//appid
//namespace
//if (k)
//	kind
//else
//	parent = fromString(fromhere)
//	kind
//if (empty)
//	id
//else
//	name
}
